#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <sstream>
#include "item.h"
#include <vector>
#include "SDL.h"
#include "SDL_image.h"
#include "node.h"
#include "astar.h"
#include "character.h"
#include "location.h"
#include "mapitem.h"
#include "itemfactory.h"
#include "sdlmap.h"

using namespace std;

Character::Character(string name, std::string character, int x, int y) throw (character_error)
{
       name_ = name;
       leftHand_ = 0;
       rightHand_ = 0;
       string pathTemp;
       charX_ = x;
       charY_ = y;
       walkingToX_ = -1;
       walkingToY_ = -1;

       ItemFactory *factory = new ItemFactory();
       items_.push_back(factory->build("dummy"));

       // Direction to start with.
       charDirection_ = 0;

       // Always start rendering frame 0
       charFrame_ = 0;

       // Load images for all directions.
       for(int i = 0; i < 8; i++)
       {
	      pathTemp = "";

	      // Direction down
	      dirDown_[(i*2)] = NULL;
	      dirDown_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/" + character + "/down/", "_down.png", i);
	      dirDown_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirDown_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirDown_[(i*2)] == NULL || dirDown_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction down left
	      dirDownLeft_[(i*2)] = NULL;
	      dirDownLeft_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/down_left/", "_down_left.png", i);
	      dirDownLeft_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirDownLeft_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirDownLeft_[(i*2)] == NULL || dirDownLeft_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction left
	      dirLeft_[(i*2)] = NULL;
	      dirLeft_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/left/", "_left.png", i);
	      dirLeft_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirLeft_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirLeft_[(i*2)] == NULL || dirLeft_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction up left
	      dirUpLeft_[(i*2)] = NULL;
	      dirUpLeft_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/up_left/", "_up_left.png", i);
	      dirUpLeft_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirUpLeft_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirUpLeft_[(i*2)] == NULL || dirUpLeft_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction up
	      dirUp_[(i*2)] = NULL;
	      dirUp_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/up/", "_up.png", i);
	      dirUp_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirUp_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirUp_[(i*2)] == NULL || dirUp_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction up right
	      dirUpRight_[(i*2)] = NULL;
	      dirUpRight_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/up_right/", "_up_right.png", i);
	      dirUpRight_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirUpRight_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirUpRight_[(i*2)] == NULL || dirUpRight_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction right
	      dirRight_[(i*2)] = NULL;
	      dirRight_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/right/", "_right.png", i);
	      dirRight_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirRight_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirRight_[(i*2)] == NULL || dirRight_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

	      // Direction down right
	      dirDownRight_[(i*2)] = NULL;
	      dirDownRight_[((i*2)+1)] = NULL;
	      pathTemp = createLoadString("adventure2/graphics/characters/BlackRanger/down_right/", "_down_right.png", i);
	      dirDownRight_[(i*2)] = IMG_Load(pathTemp.c_str());
	      dirDownRight_[((i*2)+1)] = IMG_Load(pathTemp.c_str());
	      if(dirDownRight_[(i*2)] == NULL || dirDownRight_[((i*2)+1)] == NULL)
	      {
		     throw character_error("Could not load: " + pathTemp);		      
	      }

       }

       charRect_.x = (charX_ - 16);
       charRect_.y = (charY_ - 16);
       charRect_.w = 94;
       charRect_.h = 94;
}

std::string Character::createLoadString(std::string initpath, std::string suffix, int num)
{
       stringstream ss;
       std::string temp;


       ss << (num+1);
       temp = initpath;
       temp.append(ss.str());
       temp.append(suffix);
       ss.seekp(0);
       ss.clear();

       return temp;
}


Character::~Character(){
       delete leftHand_;
       delete rightHand_;
}

string Character::name() const
{
       return name_;
}

string Character::items()
{
       string items = "";

       if(items_.size() != 0)
       {
	      // Go through the vector and find all item ids
	      for(int i = 0; i < items_.size() ; i++)
	      {

		     if (i == items_.size()-1)
		     {
			    items = items + items_[i]->getId();
		     }
		     else
		     {
			    items = items + items_[i]->getId() + " ";
		     }
	      }
       }
       return items;
}

// Add item last in vector
void Character::add(Item* item)
{
       items_.push_back(item);
}

// Remove an item
void Character::remove(Item* item)
{
       bool removed = false;

       for(int i = 0; i <= items_.size()-1 && removed == false; i++)
       {
	      if(items_[i]->getId() == item->getId())
	      {
		     items_.erase(items_.begin()+i);
		     removed = true;
	      }
       }
       if(removed == false)
       {
	      cout << "you did not carry that item" << endl;
       }
}

// Returs true if you found item, otherwise false.
bool Character::find(string item)
{
       bool found = false;

       // Look for itemsNumber
       for(int i = 0; i < items_.size() && found == false; i++)
       {
	      if(items_[i]->getId() == item)
	      {
		     found = true;
	      }
       }
       return found;
}

// Return object with the item
Item* Character::getItem(string item)
{      
       // Find item with correct itemId
       for(int i = 0; i < items_.size(); i++)
       {
	      if(items_[i]->getId() == item)
	      {
		     return items_[i];
	      }
       }

       return 0;
}

Item* Character::getItem(int num)
{      
       if(num <= items_.size()-1)
       {
	      return items_[num];
       }
       return 0;
}

Item* Character::getLeftHand() const
{
       if(leftHand_ != 0)
       {
	      return leftHand_;
       }

       return 0;
}

Item* Character::getRightHand() const
{
       if(rightHand_ != 0)
       {
	      return rightHand_;
       }

       return 0;
}

int Character::equipLeftHand(Item* item){
       if(leftHand_ == 0)
       {
	      leftHand_ = item;
	      return 0;
       }
       else
       {
	      return 1;
       }
}

int Character::equipRightHand(Item* item){
       if(rightHand_ == 0)
       {
	      rightHand_ = item;
	      return 0;
       }
       else
       {
	      return 1;
       }
}   

Item* Character::removeLeftHandItem(){
       Item* returnItem = leftHand_;
       leftHand_ = 0;
       return returnItem;
}

Item* Character::removeRightHandItem(){
       Item* returnItem = rightHand_;
       rightHand_ = 0;
       return returnItem;
}

void Character::render(SDL_Surface *screen)
{
       charRect_.x = (charX_ - 16);
       charRect_.y = (charY_ - 16);

       // Restart animation at frame 0
       // when reaching last frame.
       if(charFrame_ == 16)
       {
	      charFrame_ = 0;
       }

       switch(charDirection_)
       {
       case 0:
	      // Down
	      SDL_BlitSurface(dirDown_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 1:
	      // Down left
	      SDL_BlitSurface(dirDownLeft_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 2:
	      // Left
	      SDL_BlitSurface(dirLeft_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 3:
	      // Up left
	      SDL_BlitSurface(dirUpLeft_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 4:
	      // Up
	      SDL_BlitSurface(dirUp_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 5:
	      // Up right
	      SDL_BlitSurface(dirUpRight_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 6:
	      // Right
	      SDL_BlitSurface(dirRight_[charFrame_], NULL, screen, &charRect_);
	      break;
       case 7:
	      // Down right
	      SDL_BlitSurface(dirDownRight_[charFrame_], NULL, screen, &charRect_);
	      break;
       }

       charFrame_++;
}

vector<Node*> Character::getWalkPath(Location* location, int gotox, int gotoy, SDLMap* sdlmap)
{       
       int destX = 0;
       int destY = 0;
       int charTileX = charX_;
       int charTileY = charY_;

       // Use the character coordinates if the character is staning still,
       // otherwise use the destination coordinates
       if((walkingToX_ == -1) && (walkingToY_ == -1))
       {
	      // Convert character screen coordinates to map coordinates
	      sdlmap->getTileXY(charTileX, charTileY);

	      // Compensate since charTile* is the coordinate in top left corner
	      charTileX = charTileX;
	      charTileY = charTileY;

	      Astar* getroute = new Astar(location, charTileX, charTileY, gotox, gotoy);

	      return (getroute->getWay());   
       }
       else
       {
	      // Compensate since walkingTo* is the coordinate in top left corner
	      destX = walkingToX_;
	      destY = walkingToY_;

	      // Convert destination screen coordinates to map coordinates
	      sdlmap->getTileXY(destX, destY);

	      Astar* getroute = new Astar(location, destX, destY, gotox, gotoy);

	      return (getroute->getWay());  
       }
}

void Character::createWalkPath(Location * loc, int x, int y, SDLMap* sdlmap)
{
       walkPath_ = getWalkPath(loc, x, y, sdlmap);
}

void Character::walk(Location * loc, SDLMap * sdlmap)
{
       if((walkingToX_ == -1) && (walkingToY_ == -1))
       {
	      if(walkPath_.size() > 0)
	      {
		     int newX = walkPath_[0]->getx();
		     int newY = walkPath_[0]->gety();
		     int charTileX = charX_;
		     int charTileY = charY_;

		     // Move one step if possible and change the walkable variable as appropriate
		     // Otherwise find a new path and move.
		     if(loc->map_[newX][newY]->getWalkable() == true)
		     {
			    // Convert screen coordinates to map coordinates
			    sdlmap->getTileXY(charTileX, charTileY);

			    // Update the characters direction basedon the old and new X,Y coordinates
			    if((charTileY%2) == 0)
			    {
				   // -1 +1
				   // down-left
				   if((newX - charTileX) == -1 && (newY - charTileY) == 1){
					  charDirection_ = 1;
				   }

				   // -1 -1
				   // up-left
				   if((newX - charTileX) == -1 && (newY - charTileY) == -1){
					  charDirection_ = 3;
				   }

				   // 0 -1
				   // up-right
				   if((newX - charTileX) == 0 && (newY - charTileY) == -1){
					  charDirection_ = 5;
				   }

				   // 0 +1
				   //down-right
				   if((newX - charTileX) == 0 && (newY - charTileY) == 1){
					  charDirection_ = 7;
				   }
			    }
			    else
			    {
				   // +1 -1
				   // up-right
				   if((newX - charTileX) == 1 && (newY - charTileY) == -1){
					  charDirection_ = 5;
				   }

				   // 0 -1
				   // up-left
				   if((newX - charTileX) == 0 && (newY - charTileY) == -1){
					  charDirection_ = 3;
				   }

				   // +1 +1
				   // down-right
				   if((newX - charTileX) == 1 && (newY - charTileY) == 1){
					  charDirection_ = 7;
				   }

				   // 0 +1
				   // down-left
				   if((newX - charTileX) == 0 && (newY - charTileY) == 1){
					  charDirection_ = 1;
				   }				
			    }

			    // Current coordinates should now be walkable
			    loc->map_[charTileX][charTileY]->setWalkable(1);

			    // The coordinates the character is moving to is not walkable anymore
			    loc->map_[newX][newY]->setWalkable(0);

			    walkPath_.erase(walkPath_.begin());

			    // Set the screen coordinate the character should move to
			    sdlmap->getTileScreenXY(newX, newY);
			    walkingToX_ = newX;
			    walkingToY_ = newY;
				  
 		     }
		     else
		     {
			    createWalkPath(loc,
				   walkPath_[walkPath_.size()-1]->getx(),
				   walkPath_[walkPath_.size()-1]->gety(), sdlmap);

			    // New path is created, try and walk again
			    walk(loc, sdlmap);

		     }
	      }
       }
       else
       {
	      walking();
       }
}   


// Make the movements more fluent by moving a couple of pixels at a time.
void Character::walking()
{
       // How many pixels should the character move each iteration
       int xPixels = 2;
       int yPixels = 1;

       // Change the X coordinates until it has reached the target
       if(walkingToX_ - charX_ > 0)
       {
	      // Set the X coordinates to the destination ones if it would reach it this iteration
	      // Increase otherwise
	      if(charX_ + xPixels >= walkingToX_)
	      {
		     charX_ = walkingToX_;
		     walkingToX_ = -1;
	      }
	      else
	      {
		     charX_ = charX_ + xPixels;
	      }
       }
       else
       {
	      // Set the X coordinates to the destination ones if it would reach it this iteration
	      // Decrease otherwise
	      if (charX_ - xPixels <= walkingToX_)
	      {
		     charX_ = walkingToX_; 
		     walkingToX_ = -1;
	      }
	      else
	      {
		     charX_ = charX_ - xPixels;
	      }
       }

       // Change the Y coordinates until it has reached the target
       if(walkingToY_ - charY_ > 0)
       {
	      // Set the Y coordinates to the destination ones if it would reach it this iteration
	      // Increase otherwise
	      if(charY_ + yPixels >= walkingToY_)
	      {
		     charY_ = walkingToY_;
		     walkingToY_ = -1;
	      }
	      else
	      {
		     charY_ = charY_ + yPixels;
	      }
       }
       else
       {
	      // Set the Y coordinates to the destination ones if it would reach it this iteration
	      // Decrease otherwise
	      if (charY_ - yPixels <= walkingToY_)
	      {
		     charY_ = walkingToY_; 
		     walkingToY_ = -1;
	      }
	      else
	      {
		     charY_ = charY_ - yPixels;
	      }
       }
}

std::vector<class Item*> Character::getAllItems()
{
       return items_;
}

int Character::getX() const
{
       return charX_;
}

int Character::getY() const
{
       return charY_;
}